package common.kubernetes.apiserver;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;

import io.fabric8.kubernetes.api.model.DoneableReplicationController;
import io.fabric8.kubernetes.api.model.DoneableService;
import io.fabric8.kubernetes.api.model.IntOrString;
import io.fabric8.kubernetes.api.model.Namespace;
import io.fabric8.kubernetes.api.model.NamespaceBuilder;
import io.fabric8.kubernetes.api.model.PodSpecFluent.ContainersNested;
import io.fabric8.kubernetes.api.model.Quantity;
import io.fabric8.kubernetes.api.model.ReplicationController;
import io.fabric8.kubernetes.api.model.ReplicationControllerFluent;
import io.fabric8.kubernetes.api.model.ResourceQuota;
import io.fabric8.kubernetes.api.model.ResourceQuotaBuilder;
import io.fabric8.kubernetes.api.model.Service;
import io.fabric8.kubernetes.api.model.ServiceList;
import io.fabric8.kubernetes.api.model.ServicePort;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.internal.SerializationUtils;
import net.wicp.tams.common.Conf;
import net.wicp.tams.common.Result;
import net.wicp.tams.common.apiext.StringUtil;
import net.wicp.tams.common.apiext.yaml.YamlUtil;
import net.wicp.tams.common.exception.ExceptAll;
import net.wicp.tams.common.exception.ProjectException;

/***
 * 
 * @author rjzjh
 *
 */

public abstract class KubeClient {
	public static DefaultKubernetesClient getClient() {
		DefaultKubernetesClient client = null;
		if (StringUtil.isNull(Conf.get("common.kubernetes.apiserver.master.url"))) {
			client = new DefaultKubernetesClient();// 默认读./kube信息
		} else {
			ConfigBuilder config = new ConfigBuilder()
					.withMasterUrl(Conf.get("common.kubernetes.apiserver.master.url"));
			if (StringUtil.isNotNull(Conf.get("common.kubernetes.apiserver.master.username"))) {
				config.withUsername(Conf.get("common.kubernetes.apiserver.master.username"));
				config.withPassword(Conf.get("common.kubernetes.apiserver.master.password"));
			}
			config.withTrustCerts(true);
			client = new DefaultKubernetesClient(config.build());
		}
		return client;
	}

	private static String getUseNamespaceName(String namespace) {
		String namespaceTrue = StringUtil.isNull(namespace) ? Conf.get("common.kubernetes.apiserver.namespace.default")
				: namespace;
		return namespaceTrue;
	}

	public static Namespace getNamespace(String namespace) {
		Namespace myns = getClient().namespaces().withName(namespace).get();
		return myns;
	}

	public static ServiceList getServices(String namespace) {
		ServiceList myNsServices = getClient().services().inNamespace(getUseNamespaceName(namespace)).list();
		return myNsServices;
	}

	public static ServiceList getServices() {
		return getServices(null);
	}

	public static Service getService(String namespace, String serviceName) {
		Service myservice = getClient().services().inNamespace(getUseNamespaceName(namespace)).withName(serviceName)
				.get();
		return myservice;
	}

	public static Service getService(String serviceName) {
		return getService(null, serviceName);
	}

	/***
	 * 通过名字得到RC
	 * 
	 * @param namespace
	 * @param rcName
	 * @return
	 */
	public static ReplicationController getRC(String namespace, String rcName) {
		ReplicationController gotRc = getClient().replicationControllers().inNamespace(getUseNamespaceName(namespace))
				.withName(rcName).get();
		return gotRc;
	}

	public static ReplicationController getRC(String rcName) {
		return getRC(null, rcName);
	}

	/***
	 * 得到service
	 * 
	 * @param namespace
	 * @param rcName
	 * @return
	 */
	public static Service getSVC(String namespace, String rcName) {
		Service service = getClient().services().inNamespace(getUseNamespaceName(namespace)).withName(rcName).get();
		return service;
	}

	/***
	 * 得到RC对应的yaml文件
	 * 
	 * @param namespace
	 * @param rcName
	 * @return
	 * @throws JsonProcessingException
	 */
	public static String getRCYaml(String namespace, String rcName) throws JsonProcessingException {
		ReplicationController gotRc = getRC(namespace, rcName);
		String str = SerializationUtils.dumpAsYaml(gotRc);
		return str;
	}

	public static boolean delService(String namespace, String serviceName) {
		Boolean optResult = getClient().services().inNamespace(getUseNamespaceName(namespace)).withName(serviceName)
				.delete();
		return optResult;
	}

	public static boolean delService(String serviceName) {
		return delService(null, serviceName);
	}

	public static Result createNamespace(String namespace) {
		try {
			Namespace namespaceRet = getClient().namespaces()
					.create(new NamespaceBuilder().withNewMetadata().withName(namespace).endMetadata().build());
			return Result.getSuc().setRetObjs(namespaceRet);
		} catch (Exception e) {
			return Result.getError(e.getMessage());
		}
	}

	public static Result delNamespace(String namespace) {
		Boolean retobj = getClient().namespaces().withName("test").delete();
		if (retobj != null && retobj) {
			return Result.getSuc();
		} else {
			return Result.getError("删除名字空间失败");
		}
	}

	public static boolean delPodByName(String namespace, String podName) {
		return getClient().pods().inNamespace(getUseNamespaceName(namespace)).withName(podName).delete();
	}

	public static boolean delPodByName(String podName) {
		return delPodByName(null, podName);
	}

	public static boolean delPodByLabel(String namespace, String key, String value) {
		return getClient().pods().inNamespace(getUseNamespaceName(namespace)).withLabel(key, value).delete();
	}

	public static boolean delPodByLabel(String key, String value) {
		return delPodByLabel(null, key, value);
	}

	public static void createRc(String namespace, String filePath) throws ProjectException {
		FileInputStream inputstream;
		try {
			inputstream = new FileInputStream(filePath);
		} catch (FileNotFoundException e) {
			throw new ProjectException(ExceptAll.Project_default, "文件不存在");
		}
		createRc(namespace, inputstream);
	}

	/***
	 * 跟据文件创建RC
	 * 
	 * @param fileInputStream
	 *            输入流
	 * @throws ProjectException
	 */
	public static void createRc(String namespace, FileInputStream fileInputStream) throws ProjectException {
		JSONObject jsonObject = YamlUtil.readYaml(fileInputStream);
		DoneableReplicationController rc = getClient().replicationControllers()
				.inNamespace(getUseNamespaceName(namespace)).createNew();
		if (jsonObject.containsKey("metadata")) {
			JSONObject metadataJson = jsonObject.getJSONObject("metadata");
			if (!metadataJson.containsKey("name")) {
				throw new ProjectException(ExceptAll.param_error, "metadata需要有name元素");
			}
			ReplicationControllerFluent.MetadataNested<DoneableReplicationController> md = rc.withNewMetadata()
					.withName(metadataJson.getString("name"));
			if (metadataJson.containsKey("labels")) {
				JSONObject labels = metadataJson.getJSONObject("labels");
				for (String key : labels.keySet()) {
					md.addToLabels(key, labels.getString(key));
				}
			}
			md.endMetadata();
		}
		JSONObject specJson = jsonObject.getJSONObject("spec");
		ReplicationControllerFluent.SpecNested<DoneableReplicationController> spec = rc.withNewSpec();
		if (specJson.containsKey("replicas")) {
			spec.withReplicas(specJson.getInteger("replicas"));
		}
		if (specJson.containsKey("selector")) {
			Map<String, String> selector = JSONObject.toJavaObject(specJson.getJSONObject("selector"), Map.class);
			spec.withSelector(selector);
		}

		io.fabric8.kubernetes.api.model.ReplicationControllerSpecFluent.TemplateNested<io.fabric8.kubernetes.api.model.ReplicationControllerFluent.SpecNested<DoneableReplicationController>> newTemplate = spec
				.withNewTemplate();
		JSONObject templateJson = specJson.getJSONObject("template");
		if (templateJson.containsKey("metadata")) {
			io.fabric8.kubernetes.api.model.PodTemplateSpecFluent.MetadataNested<io.fabric8.kubernetes.api.model.ReplicationControllerSpecFluent.TemplateNested<io.fabric8.kubernetes.api.model.ReplicationControllerFluent.SpecNested<DoneableReplicationController>>> withNewMetadata = newTemplate
					.withNewMetadata();
			JSONObject jsonObject2 = templateJson.getJSONObject("metadata").getJSONObject("labels");
			Map<String, String> labels = JSON.toJavaObject(jsonObject2, Map.class);
			withNewMetadata.addToLabels(labels);
			withNewMetadata.endMetadata();
		}

		io.fabric8.kubernetes.api.model.PodTemplateSpecFluent.SpecNested<io.fabric8.kubernetes.api.model.ReplicationControllerSpecFluent.TemplateNested<io.fabric8.kubernetes.api.model.ReplicationControllerFluent.SpecNested<DoneableReplicationController>>> innerSpec = newTemplate
				.withNewSpec();
		JSONObject innerSpecJson = templateJson.getJSONObject("spec");
		JSONArray containersJson = innerSpecJson.getJSONArray("containers");
		for (int i = 0; i < containersJson.size(); i++) {
			JSONObject containerJson = containersJson.getJSONObject(i);
			ContainersNested<io.fabric8.kubernetes.api.model.PodTemplateSpecFluent.SpecNested<io.fabric8.kubernetes.api.model.ReplicationControllerSpecFluent.TemplateNested<io.fabric8.kubernetes.api.model.ReplicationControllerFluent.SpecNested<DoneableReplicationController>>>> addNewContainer = innerSpec
					.addNewContainer();
			addNewContainer.withName(containerJson.getString("name"));
			addNewContainer.withImage(containerJson.getString("image"));
			JSONArray portAry = containerJson.getJSONArray("ports");
			for (int j = 0; j < portAry.size(); j++) {
				addNewContainer.addNewPort().withContainerPort(portAry.getJSONObject(i).getInteger("containerPort"))
						.endPort();
			}
			addNewContainer.endContainer();
		}
		innerSpec.endSpec();
		newTemplate.endTemplate();
		spec.endSpec();
		rc.done();
	}

	/***
	 * 创建Service
	 * 
	 * @param namespace
	 * @param fileInputStream
	 * @throws ProjectException
	 */
	@SuppressWarnings("unchecked")
	public static void createSvc(String namespace, FileInputStream fileInputStream) throws ProjectException {
		JSONObject jsonObject = YamlUtil.readYaml(fileInputStream);
		DoneableService createNew = getClient().services().inNamespace(getUseNamespaceName(namespace)).createNew();
		JSONObject metadata = jsonObject.getJSONObject("metadata");

		createNew.withNewMetadata().withName(metadata.getString("name")).endMetadata();
		JSONObject specjson = jsonObject.getJSONObject("spec");

		io.fabric8.kubernetes.api.model.ServiceFluent.SpecNested<DoneableService> withNewSpec = createNew.withNewSpec();

		if (specjson.containsKey("type")) {
			withNewSpec.withType(specjson.getString("type"));
		}

		if (specjson.containsKey("selector")) {
			Map<String, String> seleMap = JSON.toJavaObject(specjson.getJSONObject("selector"), Map.class);
			withNewSpec.withSelector(seleMap);
		}
		if (specjson.containsKey("ports")) {
			JSONArray array = specjson.getJSONArray("ports");
			List<ServicePort> ports = new ArrayList<>();
			for (int i = 0; i < array.size(); i++) {
				JSONObject tempobj = array.getJSONObject(i);
				ServicePort temp = new ServicePort();
				if (tempobj.containsKey("name")) {
					temp.setName(tempobj.getString("name"));
				}
				if (tempobj.containsKey("nodePort")) {
					temp.setNodePort(tempobj.getInteger("nodePort"));
				}
				if (tempobj.containsKey("port")) {
					temp.setPort(tempobj.getInteger("port"));
				}
				if (tempobj.containsKey("protocol")) {
					temp.setProtocol(tempobj.getString("protocol"));
				}
				if (tempobj.containsKey("targetPort")) {
					IntOrString input = new IntOrString(tempobj.getInteger("targetPort"));
					temp.setTargetPort(input);
				}
				ports.add(temp);
			}
			withNewSpec.withPorts(ports);
		}
		withNewSpec.endSpec();
		createNew.done();
	}

	public static void createSvc(String namespace, String filePath) throws ProjectException {
		FileInputStream inputstream;
		try {
			inputstream = new FileInputStream(filePath);
		} catch (FileNotFoundException e) {
			throw new ProjectException(ExceptAll.Project_default, "文件不存在");
		}
		createSvc(namespace, inputstream);
	}

	/***
	 * 创建命名空间配额
	 * 
	 * @param namespace
	 * @param minCpu
	 * @param minMemory
	 * @param maxCpu
	 * @param maxMemory
	 * @param pods
	 */
	public static void createResourcequota(String namespace, int minCpu, int minMemory, int maxCpu, int maxMemory,
			int pods) {
		Map<String, Quantity> parma = new HashMap<>();
		parma.put("pods", new Quantity(String.valueOf(pods)));
		parma.put("requests.cpu", new Quantity(String.valueOf(minCpu)));
		parma.put("requests.memory", new Quantity(String.valueOf(minMemory)));
		parma.put("limits.cpu", new Quantity(String.valueOf(maxCpu)));
		parma.put("limits.memory", new Quantity(String.valueOf(maxMemory)));
		ResourceQuota quota = new ResourceQuotaBuilder().withNewMetadata().withName(getUseNamespaceName(namespace))
				.endMetadata().withNewSpec().addToHard(parma).endSpec().build();
		getClient().resourceQuotas().inNamespace(getUseNamespaceName(namespace)).create(quota);
	}

	/**
	 * 删除命名空间配额
	 * 
	 * @param namespace
	 */
	public static void delResourcequota(String namespace) {
		getClient().resourceQuotas().inNamespace(getUseNamespaceName(namespace)).delete();
	}

	public static boolean delRc(String namespace, String rcName) {
		return getClient().replicationControllers().inNamespace(getUseNamespaceName(namespace)).withName(rcName)
				.delete();
	}

	/***
	 * 删除svc
	 * 
	 * @param namespace
	 * @param svcName
	 * @return
	 */
	public static boolean delSvc(String namespace, String svcName) {
		return getClient().services().inNamespace(getUseNamespaceName(namespace)).withField("metadata.name", svcName)
				.delete();
	}

	/***
	 * 更新容器的image
	 * 
	 * @param namespace
	 * @param rcName
	 * @param containerIndex
	 *            这个pod的第几个容器
	 * @param imageValue
	 */
	public static void updateRcImage(String namespace, String rcName, int containerIndex, String imageValue) {
		getClient().replicationControllers().inNamespace(getUseNamespaceName(namespace)).withName(rcName).edit()
				.editSpec().editTemplate().editSpec().editContainer(containerIndex).withImage(imageValue).endContainer()
				.endSpec().endTemplate().endSpec().done();
		// getClient().replicationControllers().inNamespace((getUseNamespaceName(namespace))).withName(rcName).rolling().updateImage(imageValue);
	}

	/***
	 * 滚动更新image
	 * 
	 * @param namespace
	 * @param rcName
	 * @param imageValue
	 * @return
	 */
	public static Result updateImageRolling(String namespace, String rcName, String imageValue) {
		try {
			getClient().replicationControllers().inNamespace(namespace).withName(rcName).rolling()
					.updateImage(imageValue);
			return Result.getSuc();
		} catch (Exception e) {
			return Result.getError(e.getMessage());
		}
	}

	/***
	 * 创建或更新RC
	 * 
	 * @param namespace
	 *            命名空间
	 * @param imageValue
	 *            image值
	 * @param rcPath
	 *            rc的文件路径
	 * @return 创建或更新成功
	 */
	public static Result createOrUpdateImageRolling(String namespace, String imageValue, String rcPath) {
		JSONObject json = YamlUtil.readYaml(rcPath);
		String rcName = json.getJSONObject("metadata").getString("name");
		ReplicationController queryRc = getRC(namespace, rcName);
		if (queryRc == null) {
			try {
				createRc(namespace, rcPath);
				return Result.getSuc();
			} catch (ProjectException e) {
				return Result.getError(e.getMessage());
			}
		} else {
			return updateImageRolling(namespace, rcName, imageValue);
		}
	}
}
